all repos — caroster @ 77927f1b1b32d25b9e0eff2b035da214e0cb4a3d

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid].tsx (view raw)

  1import {useState, useReducer, useEffect} from 'react';
  2import Box from '@material-ui/core/Box';
  3import {makeStyles, useTheme} from '@material-ui/core/styles';
  4import {useTranslation} from 'react-i18next';
  5import {initializeApollo} from '../../lib/apolloClient';
  6import useToastStore from '../../stores/useToastStore';
  7import useEventStore from '../../stores/useEventStore';
  8import Layout from '../../layouts/Default';
  9import AddToMyEventDialog from '../../containers/AddToMyEventDialog';
 10import TravelColumns from '../../containers/TravelColumns';
 11import NewTravelDialog from '../../containers/NewTravelDialog';
 12import VehicleChoiceDialog from '../../containers/VehicleChoiceDialog';
 13import WelcomeDialog from '../../containers/WelcomeDialog';
 14import EventBar from '../../containers/EventBar';
 15import Loading from '../../containers/Loading';
 16import OnBoardingTour from '../../containers/OnBoardingTour';
 17import {
 18  useUpdateEventMutation,
 19  Event as EventType,
 20  useEventByUuidQuery,
 21  EventByUuidDocument,
 22  EditEventInput,
 23  useFindUserVehiclesQuery,
 24} from '../../generated/graphql';
 25import ErrorPage from '../_error';
 26import useProfile from '../../hooks/useProfile';
 27import Fab from '../../containers/Fab';
 28import useMediaQuery from '@material-ui/core/useMediaQuery';
 29
 30const POLL_INTERVAL = 10000;
 31
 32interface Props {
 33  event: EventType;
 34  eventUUID: string;
 35}
 36
 37const EventPage = props => {
 38  const {t} = useTranslation();
 39  const {event} = props;
 40  if (!event) return <ErrorPage statusCode={404} title={t`event.not_found`} />;
 41  return <Event {...props} />;
 42};
 43
 44const Event = (props: Props) => {
 45  const {eventUUID} = props;
 46  const classes = useStyles();
 47  const theme = useTheme();
 48  const {t} = useTranslation();
 49  const {user} = useProfile();
 50  const {
 51    data: {me: {profile: {vehicles = []} = {}} = {}} = {},
 52    loading
 53  } = useFindUserVehiclesQuery();
 54  const addToast = useToastStore(s => s.addToast);
 55  const setEvent = useEventStore(s => s.setEvent);
 56  const eventUpdate = useEventStore(s => s.event);
 57  const setIsEditing = useEventStore(s => s.setIsEditing);
 58  const [updateEvent] = useUpdateEventMutation();
 59  const [isAddToMyEvent, setIsAddToMyEvent] = useState(false);
 60  const [openNewTravelContext, toggleNewTravel] = useState({opened: false});
 61  const [openVehicleChoice, toggleVehicleChoice] = useReducer(i => !i, false);
 62  const {data: {eventByUUID: event} = {}} = useEventByUuidQuery({
 63    pollInterval: POLL_INTERVAL,
 64    variables: {uuid: eventUUID},
 65  });
 66  const matches = useMediaQuery(theme.breakpoints.down('sm'));
 67  const addCarClasses = matches ? 'tour_travel_add' : '';
 68  
 69  useEffect(() => {
 70    if (event) setEvent(event as EventType);
 71  }, [event]);
 72
 73  const onSave = async e => {
 74    try {
 75      const {uuid, ...data} = eventUpdate;
 76      const {id, __typename, travels, users, waitingList, ...input} = data;
 77      await updateEvent({
 78        variables: {uuid, eventUpdate: input as EditEventInput},
 79        refetchQueries: ['eventByUUID'],
 80      });
 81      setIsEditing(false);
 82    } catch (error) {
 83      console.error(error);
 84      addToast(t('event.errors.cant_update'));
 85    }
 86  };
 87
 88  const onShare = async () => {
 89    if (!event) return null;
 90    // If navigator share capability
 91    if (!!navigator.share)
 92      return await navigator.share({
 93        title: `Caroster ${event.name}`,
 94        url: `${window.location.href}`,
 95      });
 96    // Else copy URL in clipboard
 97    else if (!!navigator.clipboard) {
 98      await navigator.clipboard.writeText(window.location.href);
 99      addToast(t('event.actions.copied'));
100      return true;
101    }
102  };
103
104  const addTravelClickHandler =
105    user && vehicles?.length != 0
106      ? toggleVehicleChoice
107      : () => toggleNewTravel({opened: true});
108
109  if (!event || loading) return <Loading />;
110
111  return (
112    <Layout
113      pageTitle={t('event.title', {title: event.name})}
114      menuTitle={t('event.title', {title: event.name})}
115      displayMenu={false}
116    >
117      <EventBar
118        event={event}
119        onAdd={setIsAddToMyEvent}
120        onSave={onSave}
121        onShare={onShare}
122      />
123      <TravelColumns toggle={addTravelClickHandler} />
124      <Box className={classes.bottomRight}>
125        <Fab
126          onClick={addTravelClickHandler}
127          aria-label="add-car"
128          color="primary"
129          className={addCarClasses}
130        >
131          {t('travel.creation.title')}
132        </Fab>
133      </Box>
134      <NewTravelDialog
135        context={openNewTravelContext}
136        toggle={() => toggleNewTravel({opened: false})}
137      />
138      <VehicleChoiceDialog
139        open={openVehicleChoice}
140        toggle={toggleVehicleChoice}
141        toggleNewTravel={toggleNewTravel}
142        vehicles={vehicles}
143      />
144      <AddToMyEventDialog
145        event={event}
146        open={isAddToMyEvent}
147        onClose={() => setIsAddToMyEvent(false)}
148      />
149      <WelcomeDialog />
150      <OnBoardingTour />
151    </Layout>
152  );
153};
154
155export async function getServerSideProps(ctx) {
156  const {uuid} = ctx.query;
157  const apolloClient = initializeApollo();
158  const {data = {}} = await apolloClient.query({
159    query: EventByUuidDocument,
160    variables: {uuid},
161  });
162  const {eventByUUID: event} = data;
163  const {host = ''} = ctx.req.headers;
164
165  return {
166    props: {
167      event,
168      eventUUID: uuid,
169      metas: {
170        title: event?.name || '',
171        url: `https://${host}${ctx.resolvedUrl}`,
172      },
173    },
174  };
175}
176
177const useStyles = makeStyles(theme => ({
178  bottomRight: {
179    position: 'absolute',
180    bottom: theme.spacing(1),
181    right: theme.spacing(6),
182    width: 200,
183    [theme.breakpoints.down('sm')]: {
184      right: theme.spacing(1),
185    },
186  },
187}));
188
189export default EventPage;